//! # player - Midi 实现播放的核心模块
//!
//! 主要使用 [`MidiPlayer`]，功能包括解析 Midi，控制 Midi 的播放时序等。
//!
//! 但是需要注意，Midi Note 的映射由 Mapper 决定，也就是说，实际播放的功能由 Mapper 完成。
//!
//! ## 示例
//!
//! ```
//! let path = PathBuf::from_str("./test.mid").unwrap();
//! let player = MidiPlayer::from_path(path).await?;
//! ```

pub mod error;
mod track_message;

use std::{collections::BinaryHeap, fs, path::PathBuf, sync::Arc, time::Duration};

use async_trait::async_trait;
use midly::{MetaMessage, Smf};
use tokio::sync::Mutex;

use self::{error::Error, track_message::TrackMessage};
use crate::mapper::MidiMapper;

/// MIDI 播放器
pub struct MidiPlayer<'a> {
    #[allow(dead_code)]
    bytes: Vec<u8>,
    playtrack: Vec<TrackMessage<'a>>,
    ticks_per_beat: u32,
    timestamp: Duration,
}

#[allow(dead_code)]
impl<'a> MidiPlayer<'a> {
    /// 从字节数组创建 MIDI 播放器
    ///
    /// # 参数
    ///
    /// * `bytes` - Midi 文件的字节数组
    ///
    /// # 返回值
    ///
    /// * `Result<Self, Error>` - 解析结果
    ///
    /// # 示例
    ///
    /// ```
    /// let bytes = fs::read("./test.mid")?;
    /// let player = MidiPlayer::from_bytes(bytes).await?;
    /// ```
    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
        // 事实上由于 bytes 保存在 struct 中，把他解析为生命周期与之相同的 smf 是安全的
        let smf = Smf::parse(unsafe { std::slice::from_raw_parts(bytes.as_ptr(), bytes.len()) })?;

        // 解析 ticks_per_beat
        let ticks_per_beat: u32;
        let timestamp;
        match smf.header.timing {
            // 可以动态变化的 timestamp
            midly::Timing::Metrical(t) => {
                ticks_per_beat = t.as_int() as u32;
                // 默认 tempo 对应于 120 BPM
                timestamp = Duration::from_secs_f64(
                    500_000 as f64 / (1_000_000 as f64 * ticks_per_beat as f64),
                );
            }
            // 不可以动态变化的 timestamp
            midly::Timing::Timecode(fps, t) => {
                let fps = match fps {
                    midly::Fps::Fps24 => 24.0,
                    midly::Fps::Fps25 => 25.0,
                    midly::Fps::Fps29 => 29.0,
                    midly::Fps::Fps30 => 30.0,
                };
                ticks_per_beat = 0;
                timestamp = Duration::from_secs_f64(1.0 / (fps * t as f64));
            }
        }

        // 利用小堆顶构造 playtrack
        let mut heap = BinaryHeap::with_capacity(smf.tracks.len());
        let mut playtrack = Vec::with_capacity(smf.tracks.iter().map(|e| e.len()).sum());
        let mut tracks: Vec<_> = smf.tracks.iter().map(|e| (e.iter(), 0u32)).collect();
        for (index, (iterator, now_time)) in tracks.iter_mut().enumerate() {
            let event = iterator.next();
            if let Some(event) = event {
                *now_time += event.delta.as_int();
                heap.push(TrackMessage::new(
                    index,
                    now_time.clone(),
                    event.kind.clone(),
                ));
            }
        }

        // 循环插入并弹出，直到所有信息均插入
        while let Some(top) = heap.pop() {
            let index = top.track;
            if let Some(event) = tracks[index].0.next() {
                tracks[index].1 += event.delta.as_int();
                heap.push(TrackMessage::new(
                    index,
                    tracks[index].1.clone(),
                    event.kind.clone(),
                ));
            }
            playtrack.push(top);
        }

        Ok(Self {
            bytes,
            playtrack,
            ticks_per_beat,
            timestamp,
        })
    }

    /// 从路径创建 MIDI 播放器
    ///
    /// # 参数
    ///
    /// * `path` - Midi 文件的路径
    ///
    /// # 返回值
    ///
    /// * `Result<Self, Error>` - 解析结果
    ///
    /// # 示例
    ///
    /// ```
    /// let path = PathBuf::from_str("./test.mid").unwrap();
    /// let player = MidiPlayer::from_path(path).await?;
    /// ```
    pub fn from_path(path: PathBuf) -> Result<Self, Error> {
        let bytes = fs::read(path)?;
        Self::from_bytes(bytes)
    }

    /// 按时序遍历并发送消息
    ///
    /// # 说明
    ///
    /// 按照实际的时序，向 [`MidiMapper`] 发送 [`TrackMessage`]
    ///
    /// # 参数
    ///
    /// * `mapper` - 映射器
    /// * `mute` - 是否实际进行等待，mute 为 true 时，不进行等待
    /// * `start_point` - 开始播放的位置，默认为 0
    /// * `stop` - 停止信号（可选）
    ///
    /// # 示例
    ///
    /// ```
    /// player.track_loop(mapper, false, None, None).await;
    /// ```
    async fn track_loop<'b>(
        &self,
        mapper: Arc<Mutex<Box<dyn MidiMapper + 'b>>>,
        mute: bool,
        start_point: Option<Duration>,
        stop: Option<Arc<Mutex<bool>>>,
    ) {
        // 时间戳
        let mut timestamp = self.timestamp.clone();
        let start_point = start_point.unwrap_or_default();
        let stop = stop.unwrap_or(Arc::new(Mutex::new(false)));
        let start_tick = tokio::time::Instant::now();
        let real_start_tick = start_tick - start_point;
        let mut last_tick = Duration::ZERO;

        // 开始，调用 start
        mapper.lock().await.start().await;

        for msg in &self.playtrack {
            // 终止
            if *stop.lock().await {
                break;
            }

            // 进行延时
            last_tick = msg.time * timestamp;
            let play_part = last_tick >= start_point;
            if play_part && !mute {
                tokio::time::sleep_until(real_start_tick + last_tick).await;
            }

            // 根据信息类型进行不同处理
            match msg.kind {
                midly::TrackEventKind::Meta(meta) => {
                    mapper.lock().await.on_meta(msg.track, &meta).await;
                    match meta {
                        MetaMessage::Tempo(tempo) if self.ticks_per_beat != 0 => {
                            timestamp = Duration::from_secs_f64(
                                tempo.as_int() as f64
                                    / (1_000_000 as f64 * self.ticks_per_beat as f64),
                            );
                        }
                        _ => (),
                    }
                }
                midly::TrackEventKind::Midi { channel, message } if play_part => {
                    mapper
                        .lock()
                        .await
                        .on_midi(msg.track, channel, message)
                        .await;
                }
                midly::TrackEventKind::SysEx(data) => {
                    mapper.lock().await.on_sys_ex(msg.track, data).await;
                }
                midly::TrackEventKind::Escape(data) => {
                    mapper.lock().await.on_escape(msg.track, data).await;
                }
                _ => (),
            }
        }

        // 结束，调用 finish
        mapper.lock().await.finish(last_tick).await;
    }

    /// 播放 Midi
    ///
    /// ---
    ///
    /// # 说明
    ///
    /// 按照实际的时序，等待相应的时间，向 [`MidiMapper`] 发送 [`TrackMessage`]
    ///
    /// # 参数
    /// * `mapper` - 用于接收 [`TrackMessage`] 的 [`MidiMapper`]
    /// * `start_point` - 开始播放的时刻，默认为 0
    /// * `stop` - 终止播放的信号（可选）
    ///
    /// # 示例
    ///
    /// ```
    /// player.play(Arc::new(Mutex::new(mapper)), None, None).await;
    /// ```
    pub async fn play<'b>(
        &self,
        mapper: Arc<Mutex<Box<dyn MidiMapper + 'b>>>,
        start_point: Option<Duration>,
        stop: Option<Arc<Mutex<bool>>>,
    ) {
        self.track_loop(mapper, false, start_point, stop).await
    }

    /// 转化 Midi
    ///
    /// ---
    ///
    /// # 说明
    ///
    /// 按照实际的时序，但不实际等待相应的时间，向 [`MidiMapper`] 发送 [`TrackMessage`]
    ///
    /// # 参数
    /// * `mapper` - 用于接收 [`TrackMessage`] 的 [`MidiMapper`]
    /// * `start_point` - 开始播放的时刻，默认为 0
    /// * `stop` - 终止播放的信号（可选）
    ///
    /// # 示例
    ///
    /// ```
    /// player.convert(Arc::new(Mutex::new(mapper)), None, None).await;
    /// ```
    pub async fn convert<'b>(
        &self,
        mapper: Arc<Mutex<Box<dyn MidiMapper + 'b>>>,
        start_point: Option<Duration>,
        stop: Option<Arc<Mutex<bool>>>,
    ) {
        self.track_loop(mapper, true, start_point, stop).await
    }

    /// 获取 Midi 文件的时长
    ///
    /// ---
    ///
    /// # 返回值
    ///
    /// 返回 Midi 文件的时长
    ///
    /// # 示例
    ///
    /// ```
    /// let duration = player.duration().await;
    /// ```
    pub async fn duration(&self) -> Duration {
        let result = Arc::new(Mutex::new(Duration::ZERO));

        // 这里使用 TimerMapper 实现
        // 即利用他接收时间并进行即计时
        // 最后返回计时结果
        struct TimerMapper {
            result: Arc<Mutex<Duration>>,
        }

        // 计时的实现
        #[async_trait]
        impl MidiMapper for TimerMapper {
            async fn finish(&mut self, duration: Duration) {
                let max_duration = *self.result.lock().await;
                *self.result.lock().await = max_duration.max(duration);
            }
        }

        // 开始计时
        self.track_loop(
            Arc::new(Mutex::new(Box::new(TimerMapper {
                result: result.clone(),
            }))),
            true,
            None,
            None,
        )
        .await;

        let result = result.lock().await.clone();
        result
    }
}
